<?php
// Cargar clases utilizadas
//require_once($_SERVER["DOCUMENT_ROOT"]."/rspinf-usb/perfil/src/fabricas/PerfilFactory.php");
//require_once($_SERVER["DOCUMENT_ROOT"]."/rspinf-usb/perfil/src/mappers/Perfil.php");
require_once($_SERVER["DOCUMENT_ROOT"]."/rspinf-usb/mensajes/Mensaje.php");
require_once($_SERVER["DOCUMENT_ROOT"]."/rspinf-usb/mensajes/ListaMensaje.php");
require_once($_SERVER["DOCUMENT_ROOT"]."/rspinf-usb/mensajes/PerfilesNoExistenException.php");
require_once($_SERVER["DOCUMENT_ROOT"]."/rspinf-usb/mensajes/NoExisteException.php");

class FachadaMensajes {
  // Fachada para el módulo de mensajes
  // Utiliza el patrón Singleton
  // Debe utilizarse el método getInstance() para obtener la fachada
  // Todos los métodos devuelven NULL al encontrar un error. Lo ideal sería
  // utilizar excepciones, pero no da chance de ser tan minucioso.

  private static $instance;

  // Constructor privado para poder hacer el singleton
  private function __construct() {
  }

  // Método para obtener la instancia de la fachada
  public static function getInstance() {
    if (!isset(self::$instance)) {
      $c = __CLASS__;
      self::$instance = new $c;
    }
    return self::$instance;
  }

  /*
   * Método para obtener una lista de mensajes de cierto tipo que pertenecen a
   * cierto perfil.
   * Argumentos:
   * @ perfil: la instancia Perfil o el string con el username del Perfil.
   * @ tipo: string con el tipo de mensajes. Debe ser igual a 'recibidos',
   *         'enviados' o 'todos'.
   * Valor de retorno:
   *  Una instancia de ListaMensaje. NULL si el perfil no existe o el tipo es
   *  inválido.
   */
  public function listarMensajes($perfil, $tipo) {
    $perfil = $this->obtenerPerfil($perfil);
    if ($perfil == NULL
        || !($tipo == "recibidos" || $tipo == "enviados" || $tipo == "todos"))
      return NULL;
    return new ListaMensaje($perfil, $tipo);
  }

  /*
   * Método para obtener una lista de mensajes si y solo si el perfil $admin es
   * Administrador. Si no lo es, devuelve NULL.
   * Argumentos:
   * @ admin : instancia de Perfil o string con el username del Perfil.
   * @ perfil: instancia de Perfil o string con el username del Perfil.
   * @ tipo : string con el tipo de mensajes. Debe ser igual a 'recibidos',
   *         'enviados' o 'todos'.
   * Valor de retorno:
   *  Una instancia de ListaMensaje. NULL si el admin o perfil no existen, si
   *  tipo es inválido o si admin no es administrador.
   */
  public function moderarMensajes($admin, $perfil, $tipo) {
    $admin = $this->obtenerPerfil($admin);
    if ($admin == NULL || !$admin->getEsAdmin())
      return NULL;
    return $this->listarMensajes($perfil, $tipo);
  }

  /*
   * Método para obtener un mensaje en particular y marcarlo leído si aplica.
   * Argumentos:
   * @ perfil: instancia de Perfil o string con el username del Perfil.
   *           Se utiliza para determinar si tiene permiso para leerlo.
   * @ mid : entero, identificador del mensaje
   * Valor de retorno:
   *  La instancia de Mensaje que correponde al identificador mid.
   *  Devuelve NULL si el perfil o mid no existen, o si ocurre algún otro error.
   */
  public function leerMensaje($perfil, $mid) {
    $perfil = $this->obtenerPerfil($perfil);
    $msjfab = FabricaMensaje::getInstance();
    $m = $msjfab->getMensaje($mid);
    if ($m == NULL || $perfil == NULL)
        return NULL;

    if ($m->esDestinatario($perfil)) {
        $m->marcarLeido($perfil);
        $m->save();
        return $m;
    }

    if ($m->esEmisor($perfil))
      return $m;

    return NULL;
  }

  /*
   * Método para obtener un mensaje cualquiera, pero sólo si el perfil que lo
   * pide es Administrador.
   * Argumentos:
   * @ admin : instancia de Perfil o string con el username del Perfil.
   * @ mid : entero, identificador del mensaje.
   * Valor de retorno:
   *  La instancia de Mensaje correspondiente. NULL si no es administrador o
   *  no existen ni el perfil ni el mensaje.
   */
  public function obtenerMensaje($admin, $mid) {
    $admin = $this->obtenerPerfil($admin);
    $msjfab = FabricaMensaje::getInstance();
    $m = $msjfab->getMensaje($mid);
    if ($m == NULL || $admin == NULL || !$admin->getEsAdmin())
      return NULL;
    else
      return $m;
  }

  /*
   * Método para marcar múltiples mensajes de un perfil como leídos.
   * Argumentos:
   * @ perfil : instancia de Perfil o string con el username del Perfil.
   * @ mensajes: arreglo con los identificadores (enteros) de los mensajes.
   * Valor de retorno:
   *   Ninguno. Es algo peligroso este método porque no valida nada.
   */
  public function marcarMensajesLeidos($perfil, $mensajes) {
    $perfil = $this->obtenerPerfil($perfil);
    $msjfab = FabricaMensaje::getInstance();
    foreach ($mensajes as $mid) {
      $m = $msjfab->getMensaje($mid);
      $m->marcarLeido($perfil);
      $m->save();
    }
    return true;
  }

  /*
   * Método para marcar múltiples mensajes de un perfil como eliminados.
   * Argumentos:
   * @ perfil : instancia de Perfil o string con el username del Perfil.
   * @ mensajes: arreglo con los identificadores (enteros) de los mensajes.
   * Valor de retorno:
   *   Ninguno. Es algo peligroso este método porque no valida nada.
   */
  public function marcarMensajesEliminados($perfil, $mensajes) {
    $perfil = $this->obtenerPerfil($perfil);
    $msjfab = FabricaMensaje::getInstance();
    foreach ($mensajes as $mid) {
      $m = $msjfab->getMensaje($mid);
      $m->marcarEliminado($perfil);
      $m->save();
    }
  }

  /*
   * Método para enviar un mensaje a uno o más destinatarios.
   * Si existen destinatarios que no existen, se levanta una excepción que
   * contiene los username de todos los que no existen.
   * Argumentos:
   * @ asunto: string con el asunto del mensaje.
   * @ texto: string con el cuerpo del mensaje.
   * @ emisor: Perfil o username del perfil que envía el mensaje.
   * @ dests: arreglo de Perfil o usernames que reciben el mensaje.
   * Valor de retorno:
   *  Ninguno
   */
  public function enviarMensaje($asunto, $texto, $emisor, $dests) {
    $emisor = $this->obtenerPerfil($emisor);
    $m = new Mensaje();
    $m->setAsunto($asunto);
    $m->setMensaje($texto);
    $m->setEmisor($emisor);
    $noExisten = array();
    foreach ($dests as $pid) {
      $dest = $this->obtenerPerfil($pid);
      if ($dest == NULL)
	$noExisten[] = $pid;
      else
	$m->agregarDestinatario($dest);
    }
    if (count($noExisten)>0)
      throw new PerfilesNoExistenException();
    $m->save();
  }

  /*
   * Método para responder un mensaje. Se enviará un mensaje al emisor del
   * mensaje original con el mismo asunto prefijado con 'RE:'
   * No se valida la existencia del mensaje ni del emisor.
   * Argumentos:
   * @ mid : entero, identificador del mensaje que se responde.
   * @ emisor: Perfil o username de quien responde.
   * @ texto: cuerpo del mensaje de respuesta.
   */
  public function responderMensaje($mid, $emisor, $texto) {
    $emisor = $this->obtenerPerfil($emisor);
    $msjfab = FabricaMensaje::getInstance();
    $m = $msjfab->getMensaje($mid);
    $oldtexto = $m->getMensaje();
    $dest = $m->getEmisor()->getUsername();
    $asunto = "RE: ".$m->getAsunto();
    $respuesta = $texto . "\n\n-- Mensaje Original de --\n\n" . $oldtexto;
    $this->enviarMensaje($asunto, $respuesta, $emisor, array($dest));
  }

  /*
   * Método para reenviar un mensaje a uno o más perfiles.
   * Se envía el mismo mensaje con su asunto prefijado con 'FWD:'
   * No se valida que exista el mensaje.
   * Si no existen destinatarios, se levanta una excepción.
   * Argumentos:
   * @ mid : entero, identificador del mensaje.
   * @ emisor: Perfil o username de quien reenvía el mensaje.
   * @ dests: arreglo de Perfil o usernames de quienes reciben el mensaje.
   * Valor de retorno:
   *  Ninguno
   */
  public function reenviarMensaje($mid, $emisor, $dests) {
    $emisor = $this->obtenerPerfil($emisor);
    $msjfab = FabricaMensaje::getInstance();
    $m = $msjfab->getMensaje($mid);
    $asunto = "FWD: ".$m->getAsunto();
    $texto = $m->getMensaje();
    $this->enviarMensaje($asunto, $texto, $emisor, $dests);
  }

  /*
   * Método privado para permitir a las demás funciones recibir una instancia de
   * Perfil o el username correspondiente.
   */
  private function obtenerPerfil($perfil) {
    if (is_string($perfil)) {
      $perffab = PerfilFactory::getInstance();
      $perfil = $perffab->getPerfil($perfil);
    } else if (is_object($perfil) && get_class($perfil)=="Perfil") {
      // Bien
    } else {
      // El tipo está mal, lanzar error
    }
    return $perfil;
  }
}
?>